#include <string.h>
#include <stdlib.h>

#include "tick.h"
#include "timer.h"
#include "IntTimeSensor.h"
#include "SensorChannels.h"

typedef struct {
	int isOpen;
	GPIO_TypeDef *Port;
	uint16_t PinMask;
	uint8_t PinSource;
} IntTimeSensor_t;

static IntTimeSensor_t IntTimeSensor[PHYSENSOR_CHANNELS] = {0};

static uint32_t PinSource2ExtiLine(uint8_t PinSource) {
	return (EXTI_Line0 << PinSource);
}

uint8_t PinMask2PinSource(uint16_t PinMask) {
	uint8_t IOPinNum = 0;
	int i;
	
	// From Pin Mask to Pin Index Number
	for(i = 0; i < 16; i ++) {
		if(PinMask & (1 << i)) {
			break;
		} else {
			IOPinNum ++;
		}
	}
	return IOPinNum;
}

uint8_t Port2PortSource(GPIO_TypeDef *port) {
	switch((uint32_t)port) {
	case (uint32_t)GPIOA:
		return GPIO_PortSourceGPIOA;
	case (uint32_t)GPIOB:
		return GPIO_PortSourceGPIOB;
	case (uint32_t)GPIOC:
		return GPIO_PortSourceGPIOC;
	case (uint32_t)GPIOD:
		return GPIO_PortSourceGPIOD;
	}
	return 0;
}

// Those functions will be called from stm32f10x_it.c
void (*ExtIntCallbacks[PHYSENSOR_CHANNELS])(int, int);

// void *OpenTimerSensor(int channel, GPIO_TypeDef *Port, uint16_t Pin, uint8_t PortSource, uint8_t PinSource,
// 	void (*ExtIntCallback)(int channel, int IOLevel)) {
int OpenTimerSensor(int channel, const SensorChannelHw_t *Hw, void (*ExtIntCallback)(int channel, int IOLevel)) {
	GPIO_InitTypeDef GPIO_InitStructure;
	EXTI_InitTypeDef   EXTI_InitStructure;
	IntTimeSensor_t *sensor;
	uint8_t IOBPinSource, IOBPortSource;
	
// 	sensor = malloc(sizeof(IntTimeSensor_t));
// 	if(sensor == NULL) {
// 		return NULL;
// 	}
	sensor = &IntTimeSensor[channel];
	if(sensor->isOpen == TRUE) {
		CloseTimerSensor(channel, Hw);
	}
	
	// Disable 3V3 Power
// 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
// 	GPIO_InitStructure.GPIO_Pin = Hw->Power3V3Ctrl.PinMask;
// 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
// 	GPIO_Init(Hw->Power3V3Ctrl.Port, &GPIO_InitStructure);
	
	// Enable 3V3 Pull-up
	// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_InitStructure.GPIO_Pin = Hw->PowerPullupCtrl.PinMask;
	GPIO_Init(Hw->PowerPullupCtrl.Port, &GPIO_InitStructure);
	// GPIO_ResetBits(Hw->PowerPullupCtrl.Port, Hw->PowerPullupCtrl.PinMask);
	
	// Disable 5V Power
	if(Hw->Power5VCtrl.Port) {
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
		GPIO_InitStructure.GPIO_Pin = Hw->Power5VCtrl.PinMask;
		GPIO_Init(Hw->Power5VCtrl.Port, &GPIO_InitStructure);
	}
	
	// Set IOA as input-floating
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_InitStructure.GPIO_Pin = Hw->DataLineA.PinMask;
	GPIO_Init(Hw->DataLineA.Port, &GPIO_InitStructure);
	
	// Set IOB as input pull-up
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = Hw->DataLineB.PinMask;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(Hw->DataLineB.Port, &GPIO_InitStructure);
	
	// Config EXT-Interrupt mode in GPIO
	IOBPortSource = Port2PortSource(Hw->DataLineB.Port);
	IOBPinSource = PinMask2PinSource(Hw->DataLineB.PinMask);
	GPIO_EXTILineConfig(IOBPortSource, IOBPinSource);
	
	// Config EXT-Interrupt mode in EXTI
	EXTI_InitStructure.EXTI_Line = PinSource2ExtiLine(IOBPinSource);
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
	
	// sensor->channelIndex = channel;
	sensor->PinMask = Hw->DataLineB.PinMask;
	sensor->PinSource = IOBPinSource;
	sensor->Port = Hw->DataLineB.Port;
	
	// Save the callback functions
	ExtIntCallbacks[channel] = ExtIntCallback;
	// return sensor;
	
	sensor->isOpen = TRUE;
	return 0;
}

// return 1 or 0
int GetTimerSensorIOLevel(int channel) {
	if(IntTimeSensor[channel].isOpen == FALSE)
		return 0;
	return GPIO_ReadInputDataBit(IntTimeSensor[channel].Port, IntTimeSensor[channel].PinMask) ? 1 : 0;
}

void CloseTimerSensor(int channel, const SensorChannelHw_t *Hw) {
	GPIO_InitTypeDef GPIO_InitStructure;
	EXTI_InitTypeDef   EXTI_InitStructure;
	
	if(IntTimeSensor[channel].isOpen == FALSE)
		return;
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin = Hw->DataLineB.PinMask;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(Hw->DataLineB.Port, &GPIO_InitStructure);
	
	EXTI_InitStructure.EXTI_Line = PinSource2ExtiLine(IntTimeSensor[channel].PinSource);
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;  
	EXTI_InitStructure.EXTI_LineCmd = DISABLE;
	EXTI_Init(&EXTI_InitStructure);
	
	IntTimeSensor[channel].isOpen = FALSE;
}
